package com.pearstack.sonic.core;

import com.pearstack.sonic.core.protocol.Protocol;

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousChannelGroup;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.util.Objects;

/**
 * Sonic Socket 服务端
 *
 * @author 就眠儀式
 * */
public class SonicServer {

    private int port;

    private static final int BUFFER_SIZE = 1024;

    private AsynchronousChannelGroup channelGroup;

    private AsynchronousServerSocketChannel serverSocketChannel;

    private SonicHandler handler;

    private Protocol protocol;

    public SonicServer(int port, SonicHandler handler) {
        this.port = port;
        this.handler = handler;
    }

    public SonicServer(int port, Protocol protocol, SonicHandler handler) {
        this.port = port;
        this.protocol = protocol;
        this.handler = handler;
    }

    public SonicServer(int port, AsynchronousChannelGroup channelGroup, SonicHandler handler) {
        this.port = port;
        this.channelGroup = channelGroup;
        this.handler = handler;
    }

    public SonicServer(int port, AsynchronousChannelGroup channelGroup, Protocol protocol, SonicHandler handler) {
        this.port = port;
        this.channelGroup = channelGroup;
        this.protocol = protocol;
        this.handler = handler;
    }

    public void start() {
        try {

            this.serverSocketChannel = Objects.isNull(channelGroup) ? AsynchronousServerSocketChannel.open() : AsynchronousServerSocketChannel.open(channelGroup);
            this.serverSocketChannel.bind(new InetSocketAddress(port));
            this.serverSocketChannel.accept(this.serverSocketChannel, new AcceptCompletionHandler());

        } catch (Exception e) {

            e.printStackTrace();
        }
    }

    public class AcceptCompletionHandler implements CompletionHandler<AsynchronousSocketChannel, AsynchronousServerSocketChannel> {

        @Override
        public void completed(AsynchronousSocketChannel socketChannel, AsynchronousServerSocketChannel serverSocketChannel) {

            serverSocketChannel.accept(serverSocketChannel, this);

            SonicSession session = new SonicSession(socketChannel);

            handler.sessionCreated(session);

            ByteBuffer byteBuffer = ByteBuffer.allocate(BUFFER_SIZE);

            socketChannel.read(byteBuffer, byteBuffer, new ReadCompletionHandler(socketChannel));
        }

        @Override
        public void failed(Throwable exc, AsynchronousServerSocketChannel attachment) {

            handler.exceptionCaught(null, exc);
        }
    }

    public class ReadCompletionHandler implements CompletionHandler<Integer, ByteBuffer> {

        private AsynchronousSocketChannel socketChannel;

        public ReadCompletionHandler(AsynchronousSocketChannel socketChannel) {
            this.socketChannel = socketChannel;
        }

        @Override
        public void completed(Integer result, ByteBuffer byteBuffer) {

            byteBuffer.flip();

            SonicSession session = new SonicSession(socketChannel);

            handler.messageReceive(session, Objects.nonNull(protocol) ? protocol.encode(byteBuffer) : byteBuffer);

            byteBuffer.clear();

            socketChannel.read(byteBuffer, byteBuffer, this);
        }

        @Override
        public void failed(Throwable exc, ByteBuffer attachment) {

            SonicSession session = new SonicSession(socketChannel);

            handler.exceptionCaught(session, exc);
        }
    }

    public void close() {

    }
}
